/*
 * IMP alloc interface.
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 * Author: Aeolus <weiqing.yan@ingenic.com>
 */

#include <stdio.h>
#include <string.h>
#include <imp/imp_log.h>
#include <system/imp_alloc.h>

#include "alloc_manager.h"
#include "alloc_common.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "IMP Alloc APIs"

static Alloc g_alloc;

void IMP_Alloc_Dump();
void IMP_Alloc_Dump_To_File();

static int IMP_init()
{
	if(g_alloc.init_status == ALLOC_ALREADY_INITIALIZED)
		return 0;

	sprintf(g_alloc.mem_alloc.method, MEM_ALLOC_METHOD);
	sprintf(g_alloc.mem_manager.method, MEM_MANAGER_METHOD);

	int ret = allocInit(&g_alloc);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "alloc init failed\n");
	}
	return ret;
}

static int IMP_Get_Info(IMPAlloc *alloc, void *ptr)
{
	if(alloc == NULL || ptr == NULL) {
		IMP_LOG_ERR(TAG, "%s IMPAlloc *alloc == NULL or ptr == NULL\n", __func__);
		return -1;
	}

	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return -1;
	}

	if(g_alloc.getAllocInfo) {
		AllocInfo *info = g_alloc.getAllocInfo(&g_alloc, ptr);
		if(info == NULL) {
			IMP_LOG_ERR(TAG, "get alloc info error\n");
			return -1;
		}
		strncpy(alloc->info.owner, info->owner, sizeof(alloc->info.owner) - 1);
		alloc->info.vaddr = info->vaddr;
		alloc->info.paddr = info->paddr;
		alloc->info.length = info->length;
		alloc->info.ref_cnt = info->ref_cnt;
		alloc->info.mem_attr = info->mem_attr;
	}

	return 0;
}

int IMP_Alloc(IMPAlloc *alloc, int size, char *owner)
{
	void *addr = NULL;

	if(alloc == NULL) {
		IMP_LOG_ERR(TAG, "IMPAlloc *alloc = NULL\n");
		return -1;
	}

	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return -1;
	}

	if(g_alloc.alloc_mem) {
		addr = g_alloc.alloc_mem(&g_alloc, size, owner);
		if(addr == NULL) {
			IMP_LOG_ERR(TAG, "g_alloc.alloc_mem failed\n");
            IMP_Alloc_Dump();
			return -1;
		}
		if(IMP_Get_Info(alloc, addr)) {
			IMP_LOG_ERR(TAG, "IMP_Get_info failed\n");
            IMP_Alloc_Dump();
			return -1;
		}
	}

	IMP_Alloc_Dump_To_File();
	return 0;
}

int IMP_Sp_Alloc(IMPAlloc *alloc, void *ptr)
{
	void *addr = NULL;

	if(alloc == NULL || ptr == NULL) {
		IMP_LOG_ERR(TAG, "IMPAlloc *alloc == NULL or ptr == NULL\n");
		return -1;
	}

	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return -1;
	}

	if(g_alloc.sp_alloc_mem) {
		addr = g_alloc.sp_alloc_mem(&g_alloc, ptr);
		if(addr == NULL) {
			IMP_LOG_ERR(TAG, "g_alloc.alloc_mem failed\n");
			return -1;
		}
		if(IMP_Get_Info(alloc, addr)) {
			IMP_LOG_ERR(TAG, "IMP_Get_info failed\n");
			return -1;
		}
	}

	IMP_Alloc_Dump_To_File();
	return 0;
}

void IMP_Free(IMPAlloc *alloc, void *ptr)
{
	if(alloc == NULL || ptr == NULL) {
		IMP_LOG_ERR(TAG, "IMPAlloc *alloc == NULL or ptr == NULL\n");
		return;
	}

	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return;
	}

	if(g_alloc.free_mem) {
		if(IMP_Get_Info(alloc, ptr)) {
			IMP_LOG_ERR(TAG, "IMP_Get_info failed\n");
			return;
		}
		g_alloc.free_mem(&g_alloc, ptr);
		alloc->info.ref_cnt -= 1;
		if(alloc->info.ref_cnt == 0) {
			alloc->info.vaddr = 0;
			alloc->info.paddr = 0;
		}
	}

	IMP_Alloc_Dump_To_File();
}

IMPMemAttr IMP_Alloc_Get_Attr(IMPAlloc *alloc)
{
	if(alloc == NULL) {
		IMP_LOG_ERR(TAG, "IMPAlloc *alloc == NULL\n");
		return -1;
	}

	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return -1;
	}

	if(g_alloc.get_mem_attr) {
		alloc->info.mem_attr = g_alloc.get_mem_attr(&g_alloc, (void *)alloc->info.vaddr);
	}

	return alloc->info.mem_attr;
}

int IMP_Alloc_Set_Attr(IMPAlloc *alloc)
{
	int ret = 0;
	if(alloc == NULL) {
		IMP_LOG_ERR(TAG, "IMPAlloc *alloc == NULL\n");
		return -1;
	}

	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return -1;
	}

	if(g_alloc.set_mem_attr) {
		ret = g_alloc.set_mem_attr(&g_alloc,
				(void *)alloc->info.vaddr, alloc->info.mem_attr);
	}

	return ret;
}

void IMP_Alloc_Dump()
{
	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return;
	}

	if(g_alloc.dump_mem_status)
		g_alloc.dump_mem_status(&g_alloc);
}

void IMP_Alloc_Dump_To_File()
{
	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return;
	}

	if(g_alloc.dump_mem_to_file)
		g_alloc.dump_mem_to_file(&g_alloc);
}


void *IMP_Virt_to_Phys(void *vaddr)
{
	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return NULL;
	}

	if(g_alloc.mem_alloc.get_paddr)
		return (void *)g_alloc.mem_alloc.get_paddr((uint32_t)vaddr);
	return NULL;
}

void *IMP_Phys_to_Virt(void *paddr)
{
	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return NULL;
	}

	if(g_alloc.mem_alloc.get_vaddr)
		return (void *)g_alloc.mem_alloc.get_vaddr((uint32_t)paddr);
	return NULL;
}

int IMP_FlushCache(void *vaddr, size_t size, uint32_t dir)
{
	if(IMP_init()) {
		IMP_LOG_ERR(TAG, "imp init failed\n");
		return -1;
	}

	if(g_alloc.mem_alloc.flush_cache)
		return g_alloc.mem_alloc.flush_cache((uint32_t)vaddr, size, dir);
	return -1;
}
